import UIKit

/* XCode15的Swift版本5.9 */

/* Swift函数 */
// 函数的定义
func mulAdd1(mul1: Int, mul2: Int, add: Int) -> Int {
    let result = mul1 * mul2 + add
    return result
}

var result: Int
result = mulAdd1(mul1: 3, mul2: 5, add: 3)
print("The result of mulAdd is \(result)")

// 无形参函数
func mulAdd2() -> Int {
    let mul1, mul2, add : Int
    mul1 = 3
    mul2 = 5
    add = 3
    return mul1 * mul2 + add
}

result = mulAdd2()
print("The result of mulAdd is \(result)")

// 无返回值函数
func mulAdd3(mul1: Int, mul2: Int, add: Int) {
    print("The result of mulAdd is \(mul1 * mul2 + add)")
}

// 多个返回值函数
// 使用元组进行返回
func climate(city: String) -> (averageTemperature: Int, weather: String, wind: String) {
    var averageTemperature : Int
    var weather, wind : String
    switch city {
    case "beijing": averageTemperature = 25; weather = "dry"; wind = "strong"
    case "shanghai": averageTemperature = 15; weather = "wet"; wind = "weak"
    default : averageTemperature = 10; weather = "sunny"; wind = "normal"
    }
        
    return (averageTemperature, weather, wind)
}

var climateTemp = (0, "", "")
climateTemp = climate(city: "beijing")

// 可变形参函数
// 在函数定义的时候只知道形参的类型，并不知道形参的个数
// 在调用的形式确定形参的个数
// 可变形参在函数体内是以Array的形式存在的
func sum(numbers: Int...) -> Int {
    var result = 0
    for number in numbers {
        result = result + number
    }
    return result
}

sum(numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9)
sum(numbers: 10, 11, 12)

// inout类型
// 类似于指针的作用，表明参数值的变化会影响传值给它的外部变量
// 传递给函数中inout参数的不再是变量的值，而是变量的地址
// ！！！不能将一个常量或者字面量传递给inout类型的参数
// ！！！传递参数的时候要加上取地址符号
func swap(a: inout Int, b: inout Int) {
    let temp = a
    a = b
    b = temp
}

var swap_a = 5
var swap_b = 6
swap(a: &swap_a, b: &swap_b)
print("swap_a is \(swap_a)")
print("swap_b is \(swap_b)")

// 函数类型
// 每一个函数都有特定的函数类型，由形参类型和返回值类型共同组成
// 可以将函数类型赋值给一个变量
var mulAdd1Operation : (Int, Int, Int) -> Int = mulAdd1

// 变量的类型不需要显示地指出，编译器可以通过类型推断来得到对应的变量的值
var swapOperation = swap

// 函数类型还可以作为函数的参数使用
// 这样就可以实现运行期确定函数的类型，不需要在编写代码时就决定哪个函数被调用
func add(a: Int, b: Int) -> Int {
    return a + b
}

func sub(a: Int, b: Int) -> Int {
    return a - b
}

func printResult(operation: (Int, Int) -> Int, a: Int, b: Int) {
    let result : Int
    if a > b {
        result = operation(a, b)
    } else {
        result = operation(b, a)
    }
    print("the result is \(result)")
}

printResult(operation: sub, a: 3, b: 9)

// 函数类型还可以作为函数的返回值使用
func mathOperation(op: String) -> (Int, Int) -> Int {
    if op == "sub" {
        return sub
    } else {
        return add
    }
}

let func_result = mathOperation(op: "sub")
func_result(6, 3)

// 嵌套函数
// 在函数体内可以定义新的函数，这样的函数仅能在函数体内使用

// 闭包表达式
// 参数可以为常量和变量，也可以使用inout类型
// 在参数列表的最后可以使用可变参数
// 可以使用元组作为参数和返回值

// 使用函数的版本
func exchange(s1: String, s2: String) -> Bool {
    return s1 > s2
}

let cityArray = ["Beijing", "Shanghai", "Guangzhou", "Hangzhou", "Suzhou" ]

var descendingArray = cityArray.sorted(by: exchange)

// 使用闭包进行改写
var descendingArrayByClosures = cityArray.sorted(by: {(s1: String, s2: String)->Bool in return s1 > s2})
print(descendingArrayByClosures)

var descendingArray2 = cityArray.sorted(by: {s1, s2 in return s1 > s2})
print(descendingArray2)

var descendingArray3 = cityArray.sorted(by: {s1, s2 in s1 > s2})
print(descendingArray3)

var descendingArray4 = cityArray.sorted(by: {$0 > $1})
print(descendingArray4)

var descendingArray5 = cityArray.sorted(by: >)
print(descendingArray5)

// sorted方法有一个闭包类型参数。闭包有两个相同类型的参数，参数类型与数组元素的类型一致，返回值要求为布尔型
// 当返回值为true时，表示第一个元素和第二个元素位置保持不变。当返回值为false时，表示第一个元素应该和第二个元素交换位置

// 无参数无返回值的闭包
// 类型为( )-> Void
var voidClosureWithTypeDeclaration : () -> Void = {
    print("A closure without parameters and return value")
}

var voidClosure = {
    print("Another closure without parameters and return value")
}

voidClosureWithTypeDeclaration();
voidClosure();

// 尾随闭包
// 闭包表达式是函数的最后一个参数
// 将整个闭包表达式从函数的参数括号里移动到括号外面
func mathCompute(opr: String, n1: Int, n2: Int, compute: (Int, Int)->Int) {
    switch opr{
    case "+": print("\(n1)+\(n2) = \(compute(n1, n2))")
    case "-": print("\(n1)-\(n2) = \(compute(n1, n2))")
    case "*": print("\(n1)*\(n2) = \(compute(n1, n2))")
    case "/": print("\(n1)/\(n2) = \(compute(n1, n2))")
    default : print("not support this operator")
    }
}

// 一般使用闭包表达式
mathCompute(opr: "+", n1: 9, n2: 3, compute: {(n1: Int, n2: Int)->Int in n1 + n2})
// 使用尾随闭包
mathCompute(opr: "*", n1: 9, n2: 3){(n1: Int, n2: Int)->Int in n1 + n2}
